﻿using System;
using System.Collections.Generic;
using System.Linq;
using NHibernate.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Plugins;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates an invoice.
    /// </summary>
    public class CreateInvoice : CreateAuditable<InvoiceEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public CreateInvoice()
        {
            IssuedOn = DateTime.UtcNow;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the order identifier.
        /// </summary>
        public int OrderID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the invoice total.
        /// </summary>
        public decimal Total
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the paid amount.
        /// </summary>
        public decimal PaidAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the refund amount.
        /// </summary>
        public decimal RefundAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public InvoiceFlags Flags
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the issue date.
        /// </summary>
        public DateTime IssuedOn
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(Total <= Decimal.Zero)
                return Err.Of<InvoiceEntity>().NotValid("Total");
            if(PaidAmount < Decimal.Zero)
                return Err.Of<InvoiceEntity>().NotValid("PaidAmount");
            if(RefundAmount < Decimal.Zero)
                return Err.Of<InvoiceEntity>().NotValid("RefundAmount");
            return base.Validate(uow);
        }

        protected override void Construct(IUnitOfWork uow, InvoiceEntity entity)
        {
            base.Construct(uow, entity);

            entity.Guid = Guid.NewGuid();
            entity.Order = uow.Execute(new GetOrder
            {
                ThrowsException = true,
                EntityID = OrderID
            }).Result;
            entity.Total = Total;
            entity.PaidAmount = PaidAmount;
            entity.RefundAmount = RefundAmount;
            entity.IssuedOn = IssuedOn;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates the invoice.
    /// </summary>
    public class UpdateInvoice : UpdateAuditable<InvoiceEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public UpdateInvoice()
        {
            IssuedOn = DateTime.UtcNow;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the invoice total.
        /// </summary>
        public decimal Total
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the paid amount.
        /// </summary>
        public decimal PaidAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the refund amount.
        /// </summary>
        public decimal RefundAmount
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public InvoiceFlags Flags
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the issue date.
        /// </summary>
        public DateTime IssuedOn
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            if(Total <= Decimal.Zero)
                return Err.Of<InvoiceEntity>().NotValid("Total");
            if(PaidAmount < Decimal.Zero)
                return Err.Of<InvoiceEntity>().NotValid("PaidAmount");
            if(RefundAmount < Decimal.Zero)
                return Err.Of<InvoiceEntity>().NotValid("RefundAmount");
            return base.Validate(uow);
        }

        protected override void Revise(IUnitOfWork uow, InvoiceEntity entity)
        {
            base.Revise(uow, entity);

            entity.Total = Total;
            entity.PaidAmount = PaidAmount;
            entity.RefundAmount = RefundAmount;
            entity.Flags = Flags;
            entity.IssuedOn = IssuedOn;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the invoice.
    /// </summary>
    public class DeleteInvoice : DeleteEntity<InvoiceEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the invoice by identifier.
    /// </summary>
    public class GetInvoice : QueryEntityByID<InvoiceEntity>,
        IApiOperation
    {
        #region Utilities

        protected override IQueryable<InvoiceEntity> Build(IUnitOfWork uow, IQueryable<InvoiceEntity> query)
        {
            query = base.Build(uow, query);
            if(Fetch > FetchLevel.None)
                query = query.Fetch(x => x.Order);
            return query;
        }

        #endregion
    }

    /// <summary>
    /// Gets the invoice by guid.
    /// </summary>
    public class GetInvoiceByGuid : QueryEntity<InvoiceEntity>,
        IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets or sets the account guid.
        /// </summary>
        public Guid Guid
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<InvoiceEntity> Build(IUnitOfWork uow, IQueryable<InvoiceEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Where(x => x.Guid == Guid);
            if(Fetch > FetchLevel.None)
                query = query.Fetch(x => x.Order);
            return query;
        }

        #endregion
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over invoice collection.
    /// </summary>
    public class SearchInvoices : SearchOperation<InvoiceEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchInvoices()
        {
            SortDir = SortDirection.Ascending;
            SortBy = new []
            {
                InvoiceSortBy.ID
            };
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the invoice identifier.
        /// </summary>
        public int? OrderID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the customer identifier.
        /// </summary>
        public int? CustomerID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public InvoiceFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the creation date 'from'.
        /// </summary>
        public DateTime? CreatedFrom
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the creation date 'to'.
        /// </summary>
        public DateTime? CreatedTo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the issue date 'from'.
        /// </summary>
        public DateTime? IssuedFrom
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the issue date 'to'.
        /// </summary>
        public DateTime? IssuedTo
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort by.
        /// </summary>
        public InvoiceSortBy[] SortBy
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<InvoiceEntity> Build(IUnitOfWork uow, IQueryable<InvoiceEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Fetch(x => x.Order)
                .ThenFetch(x => x.Customer)
                .ThenFetch(x => x.Account);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            query = query.FilterByDateRange(x => x.CreatedOn, CreatedFrom, CreatedTo);
            query = query.FilterByDateRange(x => x.IssuedOn, IssuedFrom, IssuedTo);
            if(OrderID.HasValue)
                query = query.Where(x => x.Order.EntityID == OrderID.Value);
            if(CustomerID.HasValue)
                query = query.Where(x => x.Order.Customer.EntityID == CustomerID.Value);
            if(SortBy != null)
            {
                for(int i = 0; i < SortBy.Length; i++)
                {
                    switch(SortBy[i])
                    {
                        case InvoiceSortBy.Total:
                            query = query.OrderBy(x => x.Total, SortDir, i != 0);
                            break;
                        case InvoiceSortBy.CreatedOn:
                            query = query.OrderBy(x => x.CreatedOn, SortDir, i != 0);
                            break;
                        case InvoiceSortBy.IssuedOn:
                            query = query.OrderBy(x => x.IssuedOn, SortDir, i != 0);
                            break;
                        default:
                            query = query.OrderBy(x => x.EntityID, SortDir, i != 0);
                            break;
                    }
                }
            }
            return query;
        }

        #endregion
    }

    #endregion

    #region __Workflow__

    /// <summary>
    /// Generates an invoice for the order and sends notification.
    /// </summary>
    public class PlaceInvoice : Operation<InvoiceEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public PlaceInvoice()
        {
            IssuedOn = DateTime.UtcNow;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        /// <summary>
        /// Gets or sets the order identifier.
        /// </summary>
        public int OrderID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the invoice total.
        /// </summary>
        public decimal Total
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the issue date.
        /// </summary>
        public DateTime IssuedOn
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out InvoiceEntity result)
        {
            var invoice = uow.Execute(new CreateInvoice
            {
                OrderID = OrderID,
                PaidAmount = Decimal.Zero,
                RefundAmount = Decimal.Zero,
                Total = Total,
                Flags = InvoiceFlags.None,
                IssuedOn = IssuedOn
            }).Result;
            invoice = uow.Execute(new SetInvoiceStatus
            {
                EntityID = invoice.EntityID,
                Status = InvoiceFlags.Pending
            }).Result;

            result = invoice;
        }

        #endregion
    }

    /// <summary>
    /// Issues an invoice.
    /// </summary>
    public class IssueInvoice : OperateEntityByID<InvoiceEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out InvoiceEntity result)
        {
            result = uow.Execute(new SetInvoiceStatus
            {
                EntityID = EntityID,
                Status = InvoiceFlags.Issued
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Cancels an invoice.
    /// </summary>
    public class CancelInvoice : OperateEntityByID<InvoiceEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out InvoiceEntity result)
        {
            result = uow.Execute(new SetInvoiceStatus
            {
                EntityID = EntityID,
                Status = InvoiceFlags.Canceled
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Completes an invoice.
    /// </summary>
    public class CompleteInvoice : OperateEntityByID<InvoiceEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("SALES");
            }
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out InvoiceEntity result)
        {
            result = uow.Execute(new SetInvoiceStatus
            {
                EntityID = EntityID,
                Status = InvoiceFlags.Paid
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Sets the invoice status.
    /// </summary>
    public class SetInvoiceStatus : UpdateAuditable<InvoiceEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the invoice status.
        /// </summary>
        public InvoiceFlags Status
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override Err Validate(IUnitOfWork uow)
        {
            var entity = uow.Execute(new GetInvoice
            {
                ThrowsException = true,
                EntityID = EntityID
            }).Result;
            var newStatus = FlagsHelper.G1_Flags(Status);
            var oldStatus = FlagsHelper.G1_Flags(entity.Flags);

            if(newStatus == oldStatus)
                return Err.Of(this).NotAllowed();
            switch(newStatus)
            {
                case InvoiceFlags.Canceled:
                    if(oldStatus == InvoiceFlags.Paid)
                        return Err.Of(this).NotAllowed();
                    break;
                case InvoiceFlags.Pending:
                    if(oldStatus != InvoiceFlags.None && oldStatus != InvoiceFlags.Canceled)
                        return Err.Of(this).NotAllowed();
                    break;
                case InvoiceFlags.Issued:
                    if(oldStatus != InvoiceFlags.Pending)
                        return Err.Of(this).NotAllowed();
                    break;
                case InvoiceFlags.Paid:
                    if(oldStatus != InvoiceFlags.Issued)
                        return Err.Of(this).NotAllowed();
                    break;
                default:
                    return Err.Of(this).NotValid("Status");
            }
            return base.Validate(uow);
        }

        protected override void Work(IUnitOfWork uow, out InvoiceEntity result)
        {
            base.Work(uow, out result);
            using(var ioc = uow.Infrastructure.DependencyResolver())
            {
                (ioc.Resolve(result.Order.SalesModelID) as ISalesModel)
                    .OnInvoiceStatusChanged(uow, result);
            }
            try
            {
                switch(FlagsHelper.G1_Flags(result.Flags))
                {
                    case InvoiceFlags.Issued:
                        uow.Execute(new EnqueueInvoiceIssueNotification
                        {
                            InvoiceID = result.EntityID,
                            RecipientID = result.Order.Customer.Account.EntityID
                        });
                        break;
                    case InvoiceFlags.Paid:
                        uow.Execute(new EnqueueInvoicePaidNotification
                        {
                            InvoiceID = result.EntityID,
                            RecipientID = result.Order.Customer.Account.EntityID
                        });
                        break;
                    case InvoiceFlags.Canceled:
                        uow.Execute(new EnqueueInvoiceCancelNotification
                        {
                            InvoiceID = result.EntityID,
                            RecipientID = result.Order.Customer.Account.EntityID
                        });
                        break;
                }
            }
            catch(Exception ex)
            {
                uow.Execute(new CreateLogRecordFromException
                {
                    Ex = ex
                });
            }
        }

        protected override void Revise(IUnitOfWork uow, InvoiceEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags &= (~FlagsHelper.G1_Flags(entity.Flags));
            entity.Flags |= FlagsHelper.G1_Flags(Status);
            if(entity.Flags.HasFlag(InvoiceFlags.Issued))
                entity.Flags |= InvoiceFlags.Active;
            else
                entity.Flags &= (~InvoiceFlags.Active);
            base.Revise(uow, entity);
        }

        #endregion
    }

    #endregion

    #region __Notification__

    /// <summary>
    /// Enqueues an invoice issue notification message.
    /// </summary>
    /// <remarks>
    /// Message template code: NINVISSUED
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Invoice.Order.Customer.EntityID], [T:Invoice.Order.Customer.Account.EntityID], [T:Invoice.Order.Customer.Account.Email],
    /// [T:Invoice.Order.Customer.Account.Username], [T:Invoice.Order.Customer.Account.Guid], [T:Invoice.Order.BillingAddress.Country],
    /// [T:Invoice.Order.BillingAddress.Region], [T:Invoice.Order.BillingAddress.City], [T:Invoice.Order.BillingAddress.Address1],
    /// [T:Invoice.Order.BillingAddress.Address2], [T:Invoice.Order.BillingAddress.ContactName], [T:Invoice.Order.BillingAddress.PostalCode],
    /// [T:Invoice.Order.BillingAddress.PhoneNumber], [T:Invoice.Order.BillingAddress.FaxNumber], [T:Invoice.Order.BillingAddress.Email],
    /// [T:Invoice.Order.EntityID], [T:Invoice.Order.Guid], [T:Invoice.Order.DiscountAmount], [T:Invoice.Order.TaxAmount], [T:Invoice.Order.Subtotal],
    /// [T:Invoice.Order.Total], [T:Invoice.Order.CreatedOn], [T:Invoice.Guid], [T:Invoice.RefundAmount], [T:Invoice.PaidAmount], [T:Invoice.Total],
    /// [T:Invoice.CreatedOn], [T:Invoice.IssuedOn], [T:Invoice.Status], [T:Invoice.EntityID], [T:Invoice.Order.Status]
    /// </remarks>
    public class EnqueueInvoiceIssueNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the invoice identifier.
        /// </summary>
        public int InvoiceID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var invoice = uow.Execute(new GetInvoice
            {
                ThrowsException = true,
                EntityID = InvoiceID
            }).Result;
            var recipient = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var sysLocale = uow.Execute(new GetLocalizationProfileByCode
            {
                ThrowsException = true,
                Code = uow.Execute(new GetConfigParamValueByKey
                {
                    Key = Par.In("Core").Of<LocalizationProfileEntity>().Name("System")
                }).Result
            }).Result;
            var usrLocale = recipient.LocalizationProfile;
            var numFmt = usrLocale.Country.NumericFormat;
            int utcOffset = usrLocale.TimeZone.UtcOffset;
            string dtFmt = usrLocale.Country.DateTimeFormat;
            string invPaidAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.PaidAmount));
            string invRefundAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.RefundAmount));
            string invTotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.Total));
            var order = invoice.Order;
            string ordDiscountAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.DiscountAmount));
            string ordTaxAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.TaxAmount));
            string ordSubtotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.Subtotal));
            string ordTotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.Total));
            var customer = order.Customer;
            var billingAddress = order.BillingAddress;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "NINVISSUED",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Invoice.Order.Customer.EntityID", customer.EntityID.ToString()},
                    {"Invoice.Order.Customer.Account.EntityID", customer.Account.EntityID.ToString()},
                    {"Invoice.Order.Customer.Account.Email", customer.Account.Email},
                    {"Invoice.Order.Customer.Account.Username", customer.Account.Username},
                    {"Invoice.Order.Customer.Account.Guid", customer.Account.Guid.ToString()},
                    {"Invoice.Order.BillingAddress.Country", billingAddress.Country.Name},
                    {"Invoice.Order.BillingAddress.Region", billingAddress.Region.Name},
                    {"Invoice.Order.BillingAddress.City", billingAddress.City},
                    {"Invoice.Order.BillingAddress.Address1", billingAddress.Address1},
                    {"Invoice.Order.BillingAddress.Address2", billingAddress.Address2},
                    {"Invoice.Order.BillingAddress.ContactName", billingAddress.ContactName},
                    {"Invoice.Order.BillingAddress.PostalCode", billingAddress.PostalCode},
                    {"Invoice.Order.BillingAddress.PhoneNumber", billingAddress.PhoneNumber},
                    {"Invoice.Order.BillingAddress.FaxNumber",billingAddress.FaxNumber},
                    {"Invoice.Order.BillingAddress.Email", billingAddress.Email},
                    {"Invoice.Order.EntityID", order.EntityID.ToString()},
                    {"Invoice.Order.Guid", order.Guid.ToString()},
                    {"Invoice.Order.Status", FlagsHelper.G1_Flags(order.Flags).ToString()},
                    {"Invoice.Order.DiscountAmount", ordDiscountAmountStr},
                    {"Invoice.Order.TaxAmount", ordTaxAmountStr},
                    {"Invoice.Order.Subtotal", ordSubtotalStr},
                    {"Invoice.Order.Total", ordTotalStr},
                    {"Invoice.Order.CreatedOn", order.CreatedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)},
                    {"Invoice.EntityID", invoice.EntityID.ToString()},
                    {"Invoice.Guid", invoice.Guid.ToString()},
                    {"Invoice.Status", FlagsHelper.G1_Flags(invoice.Flags).ToString()},
                    {"Invoice.RefundAmount", invRefundAmountStr},
                    {"Invoice.PaidAmount", invPaidAmountStr},
                    {"Invoice.Total", invTotalStr},
                    {"Invoice.CreatedOn", invoice.CreatedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)},
                    {"Invoice.IssuedOn", invoice.IssuedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Enqueues an invoice paid notification message.
    /// </summary>
    /// <remarks>
    /// Message template code: NINVPAID
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Invoice.Order.Customer.EntityID], [T:Invoice.Order.Customer.Account.EntityID], [T:Invoice.Order.Customer.Account.Email],
    /// [T:Invoice.Order.Customer.Account.Username], [T:Invoice.Order.Customer.Account.Guid], [T:Invoice.Order.BillingAddress.Country],
    /// [T:Invoice.Order.BillingAddress.Region], [T:Invoice.Order.BillingAddress.City], [T:Invoice.Order.BillingAddress.Address1],
    /// [T:Invoice.Order.BillingAddress.Address2], [T:Invoice.Order.BillingAddress.ContactName], [T:Invoice.Order.BillingAddress.PostalCode],
    /// [T:Invoice.Order.BillingAddress.PhoneNumber], [T:Invoice.Order.BillingAddress.FaxNumber], [T:Invoice.Order.BillingAddress.Email],
    /// [T:Invoice.Order.EntityID], [T:Invoice.Order.Guid], [T:Invoice.Order.DiscountAmount], [T:Invoice.Order.TaxAmount], [T:Invoice.Order.Subtotal],
    /// [T:Invoice.Order.Total], [T:Invoice.Order.CreatedOn], [T:Invoice.Guid], [T:Invoice.RefundAmount], [T:Invoice.PaidAmount], [T:Invoice.Total],
    /// [T:Invoice.CreatedOn], [T:Invoice.IssuedOn], [T:Invoice.Status], [T:Invoice.EntityID], [T:Invoice.Order.Status]
    /// </remarks>
    public class EnqueueInvoicePaidNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the invoice identifier.
        /// </summary>
        public int InvoiceID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var invoice = uow.Execute(new GetInvoice
            {
                ThrowsException = true,
                EntityID = InvoiceID
            }).Result;
            var recipient = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var sysLocale = uow.Execute(new GetLocalizationProfileByCode
            {
                ThrowsException = true,
                Code = uow.Execute(new GetConfigParamValueByKey
                {
                    Key = Par.In("Core").Of<LocalizationProfileEntity>().Name("System")
                }).Result
            }).Result;
            var usrLocale = recipient.LocalizationProfile;
            var numFmt = usrLocale.Country.NumericFormat;
            int utcOffset = usrLocale.TimeZone.UtcOffset;
            string dtFmt = usrLocale.Country.DateTimeFormat;
            string invPaidAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.PaidAmount));
            string invRefundAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.RefundAmount));
            string invTotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.Total));
            var order = invoice.Order;
            string ordDiscountAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.DiscountAmount));
            string ordTaxAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.TaxAmount));
            string ordSubtotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.Subtotal));
            string ordTotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.Total));
            var billingAddress = order.BillingAddress;
            var customer = order.Customer;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "NINVPAID",
                LanguageID = usrLocale.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Invoice.Order.Customer.EntityID", customer.EntityID.ToString()},
                    {"Invoice.Order.Customer.Account.EntityID", customer.Account.EntityID.ToString()},
                    {"Invoice.Order.Customer.Account.Email", customer.Account.Email},
                    {"Invoice.Order.Customer.Account.Username", customer.Account.Username},
                    {"Invoice.Order.Customer.Account.Guid", customer.Account.Guid.ToString()},
                    {"Invoice.Order.BillingAddress.Country", billingAddress.Country.Name},
                    {"Invoice.Order.BillingAddress.Region", billingAddress.Region.Name},
                    {"Invoice.Order.BillingAddress.City", billingAddress.City},
                    {"Invoice.Order.BillingAddress.Address1", billingAddress.Address1},
                    {"Invoice.Order.BillingAddress.Address2", billingAddress.Address2},
                    {"Invoice.Order.BillingAddress.ContactName", billingAddress.ContactName},
                    {"Invoice.Order.BillingAddress.PostalCode", billingAddress.PostalCode},
                    {"Invoice.Order.BillingAddress.PhoneNumber", billingAddress.PhoneNumber},
                    {"Invoice.Order.BillingAddress.FaxNumber",billingAddress.FaxNumber},
                    {"Invoice.Order.BillingAddress.Email", billingAddress.Email},
                    {"Invoice.Order.EntityID", order.EntityID.ToString()},
                    {"Invoice.Order.Guid", order.Guid.ToString()},
                    {"Invoice.Order.Status", FlagsHelper.G1_Flags(order.Flags).ToString()},
                    {"Invoice.Order.DiscountAmount", ordDiscountAmountStr},
                    {"Invoice.Order.TaxAmount", ordTaxAmountStr},
                    {"Invoice.Order.Subtotal", ordSubtotalStr},
                    {"Invoice.Order.Total", ordTotalStr},
                    {"Invoice.Order.CreatedOn", order.CreatedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)},
                    {"Invoice.EntityID", invoice.EntityID.ToString()},
                    {"Invoice.Guid", invoice.Guid.ToString()},
                    {"Invoice.Status", FlagsHelper.G1_Flags(invoice.Flags).ToString()},
                    {"Invoice.RefundAmount", invRefundAmountStr},
                    {"Invoice.PaidAmount", invPaidAmountStr},
                    {"Invoice.Total", invTotalStr},
                    {"Invoice.CreatedOn", invoice.CreatedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)},
                    {"Invoice.IssuedOn", invoice.IssuedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    /// <summary>
    /// Enqueues an invoice cancel notification message.
    /// </summary>
    /// <remarks>
    /// Message template code: NINVCNCL
    /// Allowed tokens: [T:Recipient.EntityID], [T:Recipient.Guid], [T:Recipient.Email], [T:Recipient.Username],
    /// [T:Invoice.Order.Customer.EntityID], [T:Invoice.Order.Customer.Account.EntityID], [T:Invoice.Order.Customer.Account.Email],
    /// [T:Invoice.Order.Customer.Account.Username], [T:Invoice.Order.Customer.Account.Guid], [T:Invoice.Order.BillingAddress.Country],
    /// [T:Invoice.Order.BillingAddress.Region], [T:Invoice.Order.BillingAddress.City], [T:Invoice.Order.BillingAddress.Address1],
    /// [T:Invoice.Order.BillingAddress.Address2], [T:Invoice.Order.BillingAddress.ContactName], [T:Invoice.Order.BillingAddress.PostalCode],
    /// [T:Invoice.Order.BillingAddress.PhoneNumber], [T:Invoice.Order.BillingAddress.FaxNumber], [T:Invoice.Order.BillingAddress.Email],
    /// [T:Invoice.Order.EntityID], [T:Invoice.Order.Guid], [T:Invoice.Order.DiscountAmount], [T:Invoice.Order.TaxAmount], [T:Invoice.Order.Subtotal],
    /// [T:Invoice.Order.Total], [T:Invoice.Order.CreatedOn], [T:Invoice.Guid], [T:Invoice.RefundAmount], [T:Invoice.PaidAmount], [T:Invoice.Total],
    /// [T:Invoice.CreatedOn], [T:Invoice.IssuedOn], [T:Invoice.Status], [T:Invoice.EntityID], [T:Invoice.Order.Status]
    /// </remarks>
    public class EnqueueInvoiceCancelNotification : Operation<MessageEntity>
    {
        #region Properties

        /// <summary>
        /// Gets or sets the invoice identifier.
        /// </summary>
        public int InvoiceID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the recipient identifier.
        /// </summary>
        public int RecipientID
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out MessageEntity result)
        {
            var invoice = uow.Execute(new GetInvoice
            {
                ThrowsException = true,
                EntityID = InvoiceID
            }).Result;
            var recipient = uow.Execute(new GetAccount
            {
                ThrowsException = true,
                EntityID = RecipientID
            }).Result;
            var sysLocale = uow.Execute(new GetLocalizationProfileByCode
            {
                ThrowsException = true,
                Code = uow.Execute(new GetConfigParamValueByKey
                {
                    Key = Par.In("Core").Of<LocalizationProfileEntity>().Name("System")
                }).Result
            }).Result;
            var localization = recipient.LocalizationProfile;
            var numFmt = localization.Country.NumericFormat;
            int utcOffset = localization.TimeZone.UtcOffset;
            string dtFmt = localization.Country.DateTimeFormat;
            string invPaidAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.PaidAmount));
            string invRefundAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.RefundAmount));
            string invTotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(invoice.Total));
            var order = invoice.Order;
            string ordDiscountAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.DiscountAmount));
            string ordTaxAmountStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.TaxAmount));
            string ordSubtotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.Subtotal));
            string ordTotalStr = String.Format(sysLocale.Currency.PriceFormat, numFmt.Convert(order.Total));
            var customer = order.Customer;
            var billingAddress = order.BillingAddress;

            result = uow.Execute(new CreateTemplatedMessageByCode
            {
                TemplateCode = "NINVCNCL",
                LanguageID = localization.Language.EntityID,
                Replacements = new Dictionary<String, String>
                {
                    {"Recipient.EntityID", recipient.EntityID.ToString()},
                    {"Recipient.Email", recipient.Email},
                    {"Recipient.Username", recipient.Username},
                    {"Recipient.Guid", recipient.Guid.ToString()},
                    {"Invoice.Order.Customer.EntityID", customer.EntityID.ToString()},
                    {"Invoice.Order.Customer.Account.EntityID", customer.Account.EntityID.ToString()},
                    {"Invoice.Order.Customer.Account.Email", customer.Account.Email},
                    {"Invoice.Order.Customer.Account.Username", customer.Account.Username},
                    {"Invoice.Order.Customer.Account.Guid", customer.Account.Guid.ToString()},
                    {"Invoice.Order.BillingAddress.Country", billingAddress.Country.Name},
                    {"Invoice.Order.BillingAddress.Region", billingAddress.Region.Name},
                    {"Invoice.Order.BillingAddress.City", billingAddress.City},
                    {"Invoice.Order.BillingAddress.Address1", billingAddress.Address1},
                    {"Invoice.Order.BillingAddress.Address2", billingAddress.Address2},
                    {"Invoice.Order.BillingAddress.ContactName", billingAddress.ContactName},
                    {"Invoice.Order.BillingAddress.PostalCode", billingAddress.PostalCode},
                    {"Invoice.Order.BillingAddress.PhoneNumber", billingAddress.PhoneNumber},
                    {"Invoice.Order.BillingAddress.FaxNumber",billingAddress.FaxNumber},
                    {"Invoice.Order.BillingAddress.Email", billingAddress.Email},
                    {"Invoice.Order.EntityID", order.EntityID.ToString()},
                    {"Invoice.Order.Guid", order.Guid.ToString()},
                    {"Invoice.Order.Status", FlagsHelper.G1_Flags(order.Flags).ToString()},
                    {"Invoice.Order.DiscountAmount", ordDiscountAmountStr},
                    {"Invoice.Order.TaxAmount", ordTaxAmountStr},
                    {"Invoice.Order.Subtotal", ordSubtotalStr},
                    {"Invoice.Order.Total", ordTotalStr},
                    {"Invoice.Order.CreatedOn", order.CreatedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)},
                    {"Invoice.EntityID", invoice.EntityID.ToString()},
                    {"Invoice.Guid", invoice.Guid.ToString()},
                    {"Invoice.Status", FlagsHelper.G1_Flags(invoice.Flags).ToString()},
                    {"Invoice.RefundAmount", invRefundAmountStr},
                    {"Invoice.PaidAmount", invPaidAmountStr},
                    {"Invoice.Total", invTotalStr},
                    {"Invoice.CreatedOn", invoice.CreatedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)},
                    {"Invoice.IssuedOn", invoice.IssuedOn.ToLocal(utcOffset).ToStringByFmt(dtFmt)}
                },
                To = recipient.Email,
                Flags = MessageFlags.Active
            }).Result;
        }

        #endregion
    }

    #endregion
}
